#include <iostream>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <signal.h>
#include <string.h>

#include <sys/ptrace.h>

#include "signal.h"
#include "logger.h"
#include "exception.h"
#include "cmd_options.h"
#include "errcode_define.h"

using namespace std;
typedef enum{
	TRACE_TYPE_ME,
	TRACE_TYPE_ATTACH,
}TraceType;

enum{
	OPT_TRACE_TYPE,
	_OPT_UNKNOWN,
};

void HandleOptions( int argc, char ** argv ){
	DEF_OPTION( OPT_TRACE_TYPE, INT, 'T', "type"	, true, true, TRACE_TYPE_ME, "trace type"	);
//	DEF_OPTION( OPT_CONFIG, STRING	, 'f', "cfg"	, true , false, ""    , "Specify a Configuration File");
//	DEF_OPTION( OPT_VERSION,BOOL	, 'v', "version", false, false, false , "Print version information and exit"	);
//	DEF_OPTION( OPT_HELP,	BOOL	, 'h', "help"	, false, false, false , "Print Help (this message) and exit"	);
//
	if( 0 != CmdOptions::GetInstance()->HandleOpts( "version 0.2.1",argc, argv ) ){
		CmdOptions::GetInstance()->Usage();
		exit(1);
	}
	return;
}

void TraceMe(){
	pid_t pid = fork();
	if( -1 == pid ){
		LOG(ERROR) << "Call fork() failed" << endl;
		
	}else if( 0 == pid ){
		LOG(INFO) << "in child process -- pid:" <<getpid() <<" ParentPid:" << getppid() <<endl;
		ptrace( PTRACE_TRACEME, 0, NULL, NULL);
//		SendSignal( getpid(), SIGQUIT);
//		SendSignal( getpid(), SIGTRAP );
		LOG(INFO) << "Before running demo program" << endl;
		int ret = execl("./demo", NULL);
		LOG(INFO) << "After running demo program" << endl;
		if( -1 == ret ){
			THROWLOG( ERROR, "", EXCP_EXEC_DEMO_FAILED, "" );
		}
			
		sleep(10);
	}else{
		LOG(INFO) << "in parent process -- pid:" << getpid() << " ChildPid:" << pid <<endl;
		while(1){
			int status;
			pid_t childpid = waitpid( -1, &status, 0 );
			LOG(INFO) << "Return from waitpit:" << childpid <<endl;
			if( childpid <= 0 ){
				THROWLOG( ERROR, "", EXCP_CALL_WAIT_FAILED, childpid );
			}
			if( WIFSTOPPED(status) ){
				LOG(INFO) << childpid << " has been traced" << endl; 
			}
		}
		
	}
}

int main( int argc, char ** argv ){
	int ret = 0;
	try{
		HandleOptions( argc, argv );
		int traceType;
		if( !CmdOptions::GetInstance()->GetInt( OPT_TRACE_TYPE, traceType) ){
			THROWLOG(ERROR, "", EXCP_GET_OPTIONS_FAILED, "");
		}
		switch( traceType ){
		case TRACE_TYPE_ME:
			TraceMe();
			break;
		case TRACE_TYPE_ATTACH:
			break;
		default:
			THROWLOG(ERROR, "", EXCP_UNKNOWN_TRACE_TYPE, "");
			break;
		} 
	}catch( ExcpBase& e ){
		LOG(FATAL) << e.what() << endl;
		ret = e.GetErrCode();
	}catch( ... ){
		LOG(FATAL) << "Unknown Exception" << endl;
		ret = EXCP_UNKNOWN_ERROR;
	}
	return ret;
}
